; Copyright 2013 padnest@gmail.com

; Licensed under the Apache License, Version 2.0 (the "License");
; you may not use this file except in compliance with the License.
; You may obtain a copy of the License at
;
;   http://www.apache.org/licenses/LICENSE-2.0
;
; Unless required by applicable law or agreed to in writing, software
; distributed under the License is distributed on an "AS IS" BASIS,
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
; See the License for the specific language governing permissions and
; limitations under the License.


	LIST r=dec
	#include <p18f4431.inc>
    #include "padnest-pic18f-langext-v1.4.inc"
	#include "padnest-mpasm-structs-v1.0.inc"

; configs
	CONFIG OSC = HS
	CONFIG IESO = OFF
	CONFIG FCMEN = OFF
	CONFIG PWRTEN = ON
	CONFIG WDTEN = OFF
	CONFIG BOREN = OFF
	CONFIG MCLRE = ON
	CONFIG LVP = OFF
	CONFIG DEBUG = OFF
	CONFIG STVREN = ON

; registers
	UDATA
delay1		res 1
delay2		res 1
delay3		res 1
temp		res 1
colorIndex	res 1
ledR		res 1
ledG		res 1
ledB		res 1
lcd_tmp		res 1

#DEFINE LCD_RS			PORTC, RC4
#DEFINE LCD_RS_TRIS		TRISC, TRISC4
#DEFINE LCD_E			PORTC, RC5
#DEFINE LCD_E_TRIS		TRISC, TRISC5
#DEFINE LCD_D0			PORTC, RC0
#DEFINE LCD_D0_TRIS		TRISC, TRISC0
#DEFINE LCD_D1			PORTC, RC1
#DEFINE LCD_D1_TRIS		TRISC, TRISC1
#DEFINE LCD_D2			PORTC, RC2
#DEFINE LCD_D2_TRIS		TRISC, TRISC2
#DEFINE LCD_D3			PORTC, RC3
#DEFINE LCD_D3_TRIS		TRISC, TRISC3

; reset vector
	ORG	0x0000
	goto	main

; interrupt vector in compatibility mode
	ORG	0x0008

interrupt:
	switchf	colorIndex
		casel 0
			movff RCREG, ledR
			call sub_write_red
		break
		casel 1
			movff RCREG, ledG
			call sub_write_green
		break
		default
			movff RCREG, ledB
			call sub_write_blue
	endswitch
	incf colorIndex
	ifl	colorIndex, GE, 3
		clrf colorIndex
	endi
	call sub_lcd_update
	bcf	PIR1, RCIF
	retfie FAST			           ; exit from interrupt

sub_write_red:
	movff   ledR, temp		; update RED PWM duty cycle
	shftrf  temp, 6
    movff   temp, PDC0H
    movff   ledR, temp
    shftlf  temp, 2
    movff   temp, PDC0L
    return

sub_write_green:
	movff   ledG, temp		; update GREEN PWM duty cycle
	shftrf  temp, 6
    movff   temp, PDC1H
    movff   ledG, temp
    shftlf  temp, 2
    movff   temp, PDC1L
    return

sub_write_blue:
	movff   ledB, temp	     ; update BLUE PWM duty cycle
	shftrf  temp, 6
    movff   temp, PDC2H
    movff   ledB, temp
    shftlf  temp, 2
    movff   temp, PDC2L
    return

sub_lcd_update:
	callw	LCD_setLine1Pos, 0
	mov16lf	str_ready_for, TBLPTRH, TBLPTRL
	call	LCD_writeString
	switchf	colorIndex
		casel 0
			callw LCD_writeChar, 'R'
		break
		casel 1
			callw LCD_writeChar, 'G'
		break
		default
			callw LCD_writeChar, 'B'
	endswitch

	callw	LCD_setLine2Pos, 0
	mov16lf	str_r, TBLPTRH, TBLPTRL
	call	LCD_writeString
	movf	ledR, W
	call	LCD_writeHex

	mov16lf	str_g, TBLPTRH, TBLPTRL
	call	LCD_writeString
	movf	ledG, W
	call	LCD_writeHex

	mov16lf	str_b, TBLPTRH, TBLPTRL
	call	LCD_writeString
	movf	ledB, W
	call	LCD_writeHex
	return 

main:
	; init registers
	clrf	colorIndex
	clrf	ledR
	clrf	ledG
	clrf	ledB

	; init LCD
	call LCD_init

	; init PWM
	movlw	b'01001000'			; event trigger postcale 1:4 + time base FOSC/64 + free-running mode
	movwf	PTCON0				; PTOPS<7:4> PTCKPS<3:2> PTMOD<1:0>
	movlw	b'00000000'			; time base off + time base count up
	movwf	PTCON1				; PTEN<7> PTDIR<6>
	movlw	b'01001111'			; PWM0&PWM1&PWM2&PWM3&PWM4&PWM5 pin enabled for out + indipendent mode for all PWM pair
	movwf	PWMCON0				; -<7> PWMEN<6:4> PMOD<3-0>
    movlw	b'00000000'			; defaults values
	movwf	PWMCON1				;

	clrf	PTPERH				; clear PTPERH
	movlw	0xFF				; time base period (8 bits)
	movwf	PTPERL				; PTPERL<7:0>

	clrf	PDC0H				; clear PDC0H
	clrf	PDC0L				; clear PDC0L
    clrf	PDC1H				; clear PDC1H
	clrf	PDC1L				; clear PDC1L
    clrf	PDC2H				; clear PDC2H
	clrf	PDC2L				; clear PDC2L

	bsf		PTCON1, PTEN		; time base on

	; init USART
	bsf		TRISC, 6
	bsf		TRISC, 7
	movlw	b'00100000'			; Asynchronous mode, 8bit, tx enabled, Low Baud Rate
	movwf	TXSTA				; CSRC<7> TX9<6> TXEN<5> SYNC<4> SENDB<3> BRGH<2> TRMT<1> TX9D<0>
	movlw	b'10010000'			; Serial port enabled, 8bit, rx enabled
	movwf	RCSTA				; SPEN<7> RX9<6> SREN<5> CREN<4> ADDEN<3> FERR<2> OERR<1> RX9D<0>
	movlw	b'00000000'			; 8-bit Baud Rate Generator, Wake-up disabled, Auto-Baud Detect disabled
	movwf	BAUDCON				; -<7> RCIDL<6> -<5> SCKP<4> BRG16<3> -<2> WUE<1> ABDEN<0>
	movlf	12, SPBRG			; 9600 BAUD @ FOSC 8 MHZ; when BRGH=0 and BRG16=0 SPBRG=((FOSC/BAUDRATE)/64)-1


	; init interrupts
	bcf		RCON, IPEN			; disable high priority interrupts mode
	movlw	b'11000000'			; GIE + PEIE enabled
	movwf	INTCON				; GIE/GIEH<7> PEIE/GIEL<6> TMR0IE<5> INT0IE<4> RBIE<3> TMR0IF<2> INT0IF<1> RBIF<0>
	movlw	b'00100000'			; EUSART Receive Interrupt enabled (RCIE)
	movwf	PIE1				; -<7> ADIE<6> RCIE<5> TXIE<4> SSPIE<3> CCP1IE<2> TMR2IE<1> TMR1IE<0>
	clrf	PIR1				; clear RCIF interrupt flag

	call	sub_lcd_update

	do
		ifset RCSTA, OERR, ACCESS
			bcf	RCSTA, CREN
			nop
			bsf	RCSTA, CREN
		endi
	forever


str_ready_for:
    da    "Ready for ", 0
str_r:
    da    " R=", 0
str_g:
    da    " G=", 0
str_b:
    da    " B=", 0


LCD_writeHex:
	movwf	lcd_tmp
	swapf	lcd_tmp, F		; high nibble
	movf	lcd_tmp, W
	andlw	0x0F
	call	LCD_dec2Hex
	call    LCD_writeChar
	swapf	lcd_tmp, F		; low nibble
	movf	lcd_tmp, W
	andlw	0x0F
	call	LCD_dec2Hex
	call    LCD_writeChar
	return

LCD_dec2Hex:
	sublw	0x09			; 9-WREG
	btfss	STATUS,C		; is nibble Dec?
	goto	LCD_dec2Hex_hex	; no, convert hex
LCD_dec2Hex_dec:
	movf	lcd_tmp, W		; convert DEC nibble to ASCII
	andlw	0x0F
	addlw	A'0'
	return
LCD_dec2Hex_hex:
	movf	lcd_tmp, W		; convert HEX nibble to ASCII
	andlw	0x0F
	addlw	a'A'-0x0A
	return

LCD_writeString:
	TBLRD*+								; read data in TABLAT with post increment
	movf	TABLAT, W					; get data value
	bz		LCD_writeString_return		; is 0? (end string)
	call	LCD_writeChar				; no: write char
	bra		LCD_writeString				; and repeat
LCD_writeString_return:
	return								; yes: return

LCD_scrollLeft:
	movlw	b'00011000'					; shift all screen to left
	call	LCD_writeCmd
	return

LCD_scrollRight:
	movlw	b'00011100'					; shift all screen to right
	call	LCD_writeCmd
	return

LCD_init:
	call	sub_delay40ms				; wait a little bit from power up

	bcf		LCD_RS_TRIS					; RS line as output
	bcf		LCD_E_TRIS					; E line as output
	bcf		LCD_D0_TRIS					; D0 line as output
	bcf		LCD_D1_TRIS					; D1 line as output
	bcf		LCD_D2_TRIS					; D2 line as output
	bcf		LCD_D3_TRIS					; D3 line as output

	bcf		LCD_RS						; RS low
	bcf		LCD_RS						; E low

	; send reset sequence
	movlw	b'00000011'
	call	LCD_writeBus
	call	LCD_strobeE
	call	sub_delay5ms
	call	LCD_strobeE
	call	sub_delay160us
	call	LCD_strobeE
	call	sub_delay160us
	movlw	b'00000010'					; we want 4 bit bus len
	call	LCD_writeBus
	call	LCD_strobeE
	call	sub_delay160us

	; now we can send configuration commands in 4 bit bus mode
	movlw	b'00101000'					; Function set: 4 bit data, 2 lines, font 5x8
	call	LCD_writeCmd
	call	LCD_clr						; clear DDRAM
	movlw	b'00000110'					; Entry mode set: increment, screen scroll off
	call	LCD_writeCmd
	movlw	b'00001100'					; Display on/off control: display on, cursor off, blanking off
	call	LCD_writeCmd
	return


LCD_clr:
	movlw	b'00000001'					; Clear display
	call	LCD_writeCmd
	call	sub_delay5ms
	return

LCD_setLine1Pos:
	iorlw	0x80						; Set DDRAM address
	call	LCD_writeCmd
	call	sub_delay60us
	return

LCD_setLine2Pos:
	addlw	0x40						; line 2 starts at address 0x40
	iorlw	0x80						; Set DDRAM address
	call	LCD_writeCmd
	call	sub_delay60us
	return

LCD_writeChar:
	movwf	temp				; save the value to send
	swapf	temp, W				; Most Significant Nibble
	andlw	0x0F
	call	LCD_writeBus
	bsf		LCD_RS				; RS=1 for DDRAM writing
	call	LCD_strobeE
	movf	temp, W				; Least Significant Nibble
	andlw	0x0F
	call	LCD_writeBus
	bsf		LCD_RS				; RS=1 for DDRAM writing
	call	LCD_strobeE
	call	sub_delay60us		; wait for write execution
	return

LCD_writeCmd:
	movwf	temp				; save the value to send
	swapf	temp, W				; Most Significant Nibble
	andlw	0x0F
	call	LCD_writeBus
	bcf		LCD_RS				; RS=0 for commands
	call	LCD_strobeE
	movf	temp, W				; Least Significant Nibble
	andlw	0x0F
	call	LCD_writeBus
	bcf		LCD_RS				; RS=0 for commands
	call	LCD_strobeE
	call	sub_delay60us		; wait for command execution
	return

LCD_writeBus:
	bcf		LCD_D0
	btfsc	WREG, 0
	bsf		LCD_D0
	bcf		LCD_D1
	btfsc	WREG, 1
	bsf		LCD_D1
	bcf		LCD_D2
	btfsc	WREG, 2
	bsf		LCD_D2
	bcf		LCD_D3
	btfsc	WREG, 3
	bsf		LCD_D3
	return

LCD_strobeE:
	bsf LCD_E					; E=1
	nop
	bcf LCD_E					; E=0
	return


sub_delay125ms:					; 250000 cycles @ FOSC 8 MHZ (including call)
	movlw	0x4E
	movwf	delay1
	movlw	0xC4
	movwf	delay2
sub_delay125ms_2:
	decfsz	delay1, f
	goto	sub_delay125ms_3
	decfsz	delay2, f
sub_delay125ms_3:
	goto	sub_delay125ms_2
	goto	sub_delay125ms_4
sub_delay125ms_4:
	nop
	return

sub_delay40ms:					; 80000 cycles @ FOSC 8 MHZ (including call)
	movlw	0x7E
	movwf	delay1
	movlw	0x3F
	movwf	delay2
sub_delay40ms_2:
	decfsz	delay1, f
	goto	sub_delay40ms_3
	decfsz	delay2, f
sub_delay40ms_3:
	goto	sub_delay40ms_2
	goto	sub_delay40ms_4
sub_delay40ms_4:
	nop
	return

sub_delay5ms:					; 10000 cycles @ FOSC 8 MHZ (including call)
	movlw	0xCE
	movwf	delay1
	movlw	0x08
	movwf	delay2
sub_delay5ms_2:
	decfsz	delay1, f
	goto	sub_delay5ms_3
	decfsz	delay2, f
sub_delay5ms_3:
	goto	sub_delay5ms_2
	goto	sub_delay5ms_4
sub_delay5ms_4:
	nop
	return

sub_delay160us:					; 320 cycles @ FOSC 8 MHZ (including call)
	movlw	0x7E
	movwf	delay1
	movlw	0x03
	movwf	delay2
sub_delay160us_2:
	decfsz	delay1, f
	goto	sub_delay160us_3
	decfsz	delay2, f
sub_delay160us_3:
	goto	sub_delay160us_2
	goto	sub_delay160us_4
sub_delay160us_4:
	nop
	return

sub_delay60us:					; 120 cycles @ FOSC 8 MHZ (including call)
	movlw	0x26
	movwf	delay1
sub_delay60us_2:
	decfsz	delay1, f
	goto	sub_delay60us_2
	nop
	return

	END